<template>
  <div class="Home">
    <Map @loadViewer="loadViewer"></Map>
  </div>
</template>

<script setup lang="ts">
import { createVNode, render, onBeforeUnmount, ref } from 'vue';
// 引入地图组件 
import Map from "@/components/Map.vue";
// 加载静态图片资源
import img1 from "@/assets/images/1.png";
// 引入弹窗组件
import Popup from "@/components/Popup.vue";

// 定义地图的viewer
const mapViewer = ref<any>(null);
// 地图加载后，获取viewer，并对地图进行操作
const loadViewer = (viewer: any) => {
  // 将viewer存储到ref中
  mapViewer.value = viewer;
  // 设置地图的视角
  setView(viewer, [114.30074300927934, 30.597253568983838], 20000);
  // 设置左键点击事件
  setLeftclick(viewer);

  setTimeout(() => {
    // 设置标记点
    setMarkerEntity(viewer, [114.42027691195437, 30.46181509645924], "公司");
    // 将相机飞行到指定位置
    setFlyTo(viewer, [114.42027691195437, 30.46181509645924], 20000);

    // 折线数据
    const lineList = [
      [
        114.42027691195437,
        30.46181509645924
      ],
      [
        114.42031807608393,
        30.46137695154061
      ],
      [
        114.41870647956145,
        30.461364685881342
      ],
      [
        114.4185791650727,
        30.464230384471108
      ],
      [
        114.41517203218315,
        30.464169069111588
      ],
      [
        114.41510657266562,
        30.469256846771856
      ],
      [
        114.42292699526541,
        30.475302904136214
      ],

      [
        114.42379969288466,
        30.48004522770033
      ],
      [
        114.42538663040973,
        30.48870528467727
      ],
      [
        114.42618020781309,
        30.49530372213541
      ],
      [
        114.42491025080994,
        30.498671818346278
      ],
      [
        114.42530718829057,
        30.50217432523262
      ],
      [
        114.42617416717037,
        30.507279848300854
      ],
      [
        114.41792162855621,
        30.509049253363678
      ],
      [
        114.40847134259923,
        30.50980504915961
      ],
      [
        114.39933844337595,
        30.509391425399908
      ],
      [
        114.39179373418584,
        30.50828995684502
      ],
      [
        114.39344928587701,
        30.509368756147442
      ],
      [
        114.3936869406783,
        30.511000661775864
      ],
      [
        114.39376122047689,
        30.51181543638506
      ],
      [
        114.39338976160487,
        30.51199768735678
      ]
    ];
    // 设置折线
    setPolyline(viewer, lineList);

    addGeoJson(viewer);
  }, 3000);
};

// 将地图定位到指定经纬度
const setView = (viewer: any, ltlg: number[], height: number) => {
  // 设置地图的视角 
  viewer.camera.setView({
    // 目标位置 经度、纬度、高度
    destination: Cesium.Cartesian3.fromDegrees(ltlg[0], ltlg[1], height),
    // 方向
    orientation: {
      // 方向角 0-360度
      heading: Cesium.Math.toRadians(348.4202942851978),
      // 俯仰角 -90度到90度
      pitch: Cesium.Math.toRadians(-89.74026687972041),
      // 翻滚角 0-360度
      roll: Cesium.Math.toRadians(0)
    }
  });
};
// 左键点击事件
const setLeftclick = (viewer: any) => {
  // 创建一个事件处理器
  let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  // 左键点击事件
  let leftclick = Cesium.ScreenSpaceEventType.LEFT_CLICK;
  // 移除左键点击事件
  viewer.screenSpaceEventHandler.removeInputAction(leftclick);
  // 添加左键点击事件
  handler.setInputAction((event: any) => {
    // 转换为不包含地形的笛卡尔坐标
    let clickPosition = viewer.scene.camera.pickEllipsoid(event.position);
    // 转经纬度（弧度）坐标
    let radiansPos = Cesium.Cartographic.fromCartesian(clickPosition);
    // 转经纬度（角度）坐标
    console.log("经度：" + Cesium.Math.toDegrees(radiansPos.longitude) + ", 纬度：" + Cesium.Math.toDegrees(radiansPos.latitude));

    // 获取点击的实体
    let pick = viewer.scene.pick(event.position);
    // 判断是否点击到了实体 并且实体的name是point
    if (Cesium.defined(pick) && (pick.id) && (pick.id.name === "point")) {
      // 设置弹窗
      setPopup(viewer, pick.id);
    }
  }, leftclick)
};

// 设置标记点
const setMarkerEntity = (viewer: any, ltlg: number[], name: string) => {
  // 创建一个标记点的实体
  let markerEntity = viewer.entities.add({
    // 标记点的位置
    position: Cesium.Cartesian3.fromDegrees(ltlg[0], ltlg[1]),
    // 显示给用户的可读名称。它不必是唯一的。
    name: "point",
    // 标记点的点
    point: {
      // 标记点的颜色
      color: Cesium.Color.fromCssColorString('#ee0000'),
      // 标记点的像素大小
      pixelSize: 10,
      // 标记点的轮廓颜色
      outlineColor: Cesium.Color.WHITE,
      // 标记点的轮廓宽度
      outlineWidth: 2
    },
    // 标记点的标签
    label: {
      // 标记点的标签文本
      text: name,
      // 标签的填充颜色
      fillColor: Cesium.Color.fromCssColorString('#0f46b2'),
      // 标签的轮廓颜色
      outlineColor: Cesium.Color.fromCssColorString('#ffffff'),
      // 标签的轮廓宽度
      outlineWidth: 3,
      // 标签的样式
      style: Cesium.LabelStyle.FILL_AND_OUTLINE,
      // 标签的字体
      font: '14px sans-serif',
      // 标签的偏移量
      pixelOffset: new Cesium.Cartesian2(0, 10),
      // 这里设置了就不会被遮盖了，设为负值则在更上层
      eyeOffset: new Cesium.Cartesian3(0, 0, -10)
    },
    // 标记点的图像
    billboard: {
      // 图像地址，URI或Canvas的属性 vue3里面不能直接使用require
      image: img1,
      // 设置颜色和透明度
      color: Cesium.Color.WHITE.withAlpha(1),
      // 高度（以像素为单位）
      height: 50,
      // 宽度（以像素为单位）
      width: 50,
      // 逆时针旋转
      rotation: 0,
      // 大小是否以米为单位
      sizeInMeters: false,
      // 相对于坐标的垂直位置
      verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
      // 相对于坐标的水平位置
      horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
      // 该属性指定标签在屏幕空间中距此标签原点的像素偏移量
      pixelOffset: new Cesium.Cartesian2(0, 0),
      // 应用于图像的统一比例。比例大于会1.0放大标签，而比例小于会1.0缩小标签。
      scale: 1.0,
      // 显示在距相机的距离处的属性，多少区间内是可以显示的
      // distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 500000),
      // 是否显示
      show: true,
    }
  });
  // 下面是鼠标进入标记点实体，鼠标样式变为pointer
  // 创建一个ScreenSpaceEventHandler实例  
  let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  // 监听鼠标移动事件  
  handler.setInputAction((event: any) => {
    let pickedObject = viewer.scene.pick(event.endPosition);
    // 检查是否拾取到了实体  
    if (Cesium.defined(pickedObject) && (pickedObject.id === markerEntity)) {
      // 如果拾取到了实体，设置body的cursor为pointer  
      document.body.style.cursor = 'pointer';
    } else {
      // 如果没有拾取到实体，移除设置body的cursor为pointer 
      document.body.style.cursor = 'default';
    }
  }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  // 返回标记点的实体
  return markerEntity;
};

// 清除标记点
const clearMarkersEntity = (viewer: any) => {
  // 获取所有的实体
  let entities = viewer.entities.values;
  // 遍历实体
  for (let i = 0; i < entities.length; i++) {
    // 获取实体
    let entity = entities[i];
    // 判断实体类型
    if (entity.name === "point") {
      // 移除实体
      viewer.entities.remove(entity);
    }
  }
};

// 页面销毁前 清除标记点
onBeforeUnmount(() => {
  clearMarkersEntity(mapViewer.value);
});

// 将相机飞行到指定位置
const setFlyTo = (viewer: any, ltlg: number[], height: number) => {
  // 计算笛卡尔坐标
  let position = Cesium.Cartesian3.fromDegrees(ltlg[0], ltlg[1], height);
  // 将相机飞行到指定位置
  viewer.camera.flyTo({
    // 目标位置
    destination: position,
    // 方向
    orientation: {
      // 方向角 0-360度
      heading: Cesium.Math.toRadians(0),
      // 俯仰角 -90度到90度
      pitch: Cesium.Math.toRadians(-90),
      // 翻滚角 0-360度
      roll: Cesium.Math.toRadians(0)
    },
    // 飞行时间，单位为秒
    duration: 1.0,
    // 飞行完成后的回调函数
    complete: function () {
      console.log('Camera has flown to the destination.');
    }
  });

};

// 设置弹窗
const setPopup = (viewer: any, data: any) => {
  // 创建一个div
  let div = document.createElement("div");
  // 设置div的id
  div.id = "popup" + data.id;
  // 设置div的样式position
  div.style.position = "absolute";
  // 设置div的样式width
  div.style.width = "200px";

  // 引用弹窗组件
  // 接下来就调用方法
  // 第一个参数传入一个VUE组件，第二个参数传入组件中的props或者事件
  // 子组件 $emit 出来的事件也是通过第二个参数来注入监听，前面需要添加 "on" 
  // 比如子组件 $emit("closePopup") 时，这里就要传入 “onClosePopup” 来监听
  let instance: any = createVNode(Popup, {
    title: '标题',
    onClosePopup: () => {
      console.log('关闭弹窗');
      clearDiv(viewer, div, eventListener);
    }
  })
  // 使用render方法渲染组件到div中
  render(instance, div);

  // 将div添加到地图中
  viewer.cesiumWidget.container.appendChild(div);
  // 获取点击的实体的位置
  let gisPosition = data.position._value;
  // 设置div的位置
  const eventListener = () => {
    // 创建一个新的Cartesian2对象，用于存储WGS84坐标转换为窗口坐标后的结果。
    const windowPosition = new Cesium.Cartesian2();
    // 将使用Cesium的SceneTransforms模块的wgs84ToWindowCoordinates函数，将gisPosition（WGS84坐标系中的点）转换为窗口坐标，并存储在windowPosition中。
    Cesium.SceneTransforms.wgs84ToWindowCoordinates(
      viewer.scene,
      gisPosition,
      windowPosition
    );
    // 设置div的位置
    div.style.top = windowPosition.y + "px";
    div.style.left = windowPosition.x + "px";
    // 设置div的transform属性 来微调弹窗的位置
    div.style.transform = "translate(-50%, -180%)";
  }
  // 为Cesium Viewer的场景添加了一个postRender事件监听器。这个事件会在每次场景渲染完成后触发，用于执行某些操作。
  viewer.scene.postRender.addEventListener(eventListener);
};

// 清除弹窗
const clearDiv = (viewer: any, dom: any, eventListener: any) => {
  if (dom) {
    // 获取弹窗的父元素
    let parent = dom.parentElement;
    // 移除弹窗
    parent.removeChild(dom);
    // 移除事件监听
    viewer.scene.postRender.removeEventListener(eventListener, this)
  }
};
// 设置折线
const setPolyline = (viewer: any, list: any[]) => {
  // 遍历折线数据
  list.forEach((item, index) => {
    // 判断是否是最后一个点
    if (index < list.length - 1) {
      // 添加折线
      addPolyline(viewer, [Cesium.Cartesian3.fromDegrees(item[0], item[1]), Cesium.Cartesian3.fromDegrees(list[index + 1][0], list[index + 1][1])]);
    }
  });
};
// 添加折线
const addPolyline = function (viewer: any, positions: any[]) {
  // 判断是否有折线数据
  if (positions.length === 0) {
    // 没有折线数据，直接返回
    return;
  }
  // 创建一个实体
  let entity = new Cesium.Entity({
    // 实体的名称
    name: "line",
    // 是否显示
    show: true,
    // Cesium.PolylineGraphics 是用于描述多段线（polyline）的视觉特性的类。它包含用于定义如何在地球表面渲染多段线的各种属性
    polyline: new Cesium.PolylineGraphics({
      // 是否显示
      show: true,
      // 一个数组，包含了多段线所有顶点的笛卡尔坐标（Cartesian3 对象）
      positions: positions,
      // 多段线的宽度，以像素为单位
      width: 5,
      // 定义了多段线的颜色或材质
      material: Cesium.Color.RED
    })
  });
  // 添加实体
  viewer.entities.add(entity);
  // 返回实体
  return entity;
};

// 加载geojson文件
const addGeoJson = (viewer: any) => {
  // 移除所有数据源
  (viewer.dataSources || {}).removeAll();
  // public/mapData/wuhan.json
  let data = (viewer.dataSources || {}).add(Cesium.GeoJsonDataSource.load('/mapData/wuhan.json'));
  data.then((res: any) => {
    console.log(res);
    // let entities = res.entities.values;
  })
};

</script>

<style lang="less" scoped>
.Home {}
</style>